<?php

	ob_start();

	$typhoon['page'] = array(
		'title'       => '',
		'description' => '',
		'keywords'    => array(),
		'script'      => '',
		'style'       => '',
		'robots'      => 'index,follow,noarchive',
		'charset'     => 'utf-8',
		'lang'        => 'en',
		'dir'         => 'ltr',
		'scripts'     => array(),
		'styles'      => array()

		// Add any other optional page defaults you want eg.
		//'http'        => 'http://' . $_SERVER['HTTP_HOST']
		//'https'       => 'https://' . $_SERVER['HTTP_HOST']
	);

	$typhoon['xpath'] = new DOMXPath($typhoon['xml']);

	//
	// Add page <element> nodes
	//
	foreach ($typhoon['xpath']->query('elements/element') as $element)
	{
		if ($element->getAttribute('page') == '' || preg_match($element->getAttribute('page'), $_SERVER['REDIRECT_URL']))
			$typhoon['elements'][] = array('path' => 'includes/elements/' . $element->textContent, 'private' => array(), 'region' => $element->getAttribute('region'));
	}

	//
	// Add page <head> nodes
	//
	foreach ($typhoon['xpath']->query('head/*') as $node)
	{
		if ($node->textContent || $node->hasAttributes())
		{
			switch ($node->tagName)
			{
				case 'title':       $typhoon['page']['title']       = $node->textContent; break;
				case 'description': $typhoon['page']['description'] = $node->textContent; break;
				case 'keywords':    $typhoon['page']['keywords'][]  = $node->textContent; break;
				case 'style':       $typhoon['page']['style']      .= $node->textContent; break;
				case 'robots':      $typhoon['page']['robots']      = $node->textContent; break;
				case 'script':
					if ($node->textContent)
					{
						$typhoon['page']['script'] .= $node->textContent;
						break;
					} // else ... the <script> tag should have a src attribute so we fall through
				default: $typhoon['page']['head'][] = $node;
			}
		}
	}

	//
	// Load page layout XML file
	//
	$typhoon['layout']['name'] = $typhoon['xml']->documentElement->getAttribute('layout');
	$typhoon['layout']['xpath'] = new DOMXPath(DOMDocument::load('includes/layouts/' . $typhoon['layout']['name'] . '.xml'));

	//
	// Add layout <element> nodes (usually header & footer)
	//
	foreach ($typhoon['layout']['xpath']->query('elements/element') as $node)
	{
		$typhoon['elements'][] = array('path' => 'includes/elements/' . $node->textContent, 'private' => array(), 'region' => $node->getAttribute('region'));
	}

	//
	// Get layout stylesheet -- this controller requires all layouts to have a default stylesheet inside <head><style>...</style></head>
	//
	$typhoon['page']['style'] .= $typhoon['layout']['xpath']->query('head/style')->item(0)->textContent;

	//
	// Common Includes
	//
	include('includes/functions.php');
	//include('includes/db.php');
	//include('includes/sessions.php');

	//
	// Add page specific "includes" -- can be used to include things like mail classes or "sub-controllers" etc. which are not necessary for every page
	//
	foreach ($typhoon['xpath']->query('include') as $node)
	{
		include('includes/' . $node->textContent);
	}

	if (ob_get_length() > 0) die(ob_get_clean());

	//
	// Process Elements
	//
	foreach ($typhoon['elements'] as &$element)
	{
		$typhoon['data']['private'] = &$element['private'];

		$_ = $element['path'];

		$files = array_flip(scandir($_));

		if (isset($files['data.php'])) sandbox($_ . '/data.php');
		if (isset($files['post.php']) && $_SERVER['REQUEST_METHOD'] == 'POST' && $_POST) sandbox($_ . '/post.php');
		if (isset($files['init.php'])) $typhoon['init'][] = &$element;
		if (isset($files['script.js'])) $typhoon['page']['scripts'][] = $_ . '/script.js';
		if (isset($files['style.css'])) $typhoon['page']['styles'][] = $_ . '/style.css';
		if (isset($files['view.php'])) $typhoon['views'][] = &$element;
	}

	//
	// Finished using the database here, so we can close it and process the views while the db is closed! All the data necessary for the views should be stored in $typhoon['data']['private'] or somewhere else, maybe a global called $data.
	//
	//session_write_close();
	//$db->close();

	//
	// Process element "inits" which can do several things, like add additional stylesheets, javascript files, set page title, keywords, descriptions etc.
	//
	if (isset($typhoon['init']))
		foreach ($typhoon['init'] as &$element)
		{
			$typhoon['data']['private'] = &$element['private'];
			sandbox($element['path'] . '/init.php');
		}

	//
	// Add stylesheets
	//
	foreach ($typhoon['page']['styles'] as $style)
		$typhoon['page']['style'] .= file_get_contents($style);

	//
	// Add javascript files
	//
	foreach ($typhoon['page']['scripts'] as $script)
		$typhoon['page']['script'] .= file_get_contents($script);

	if (ob_get_length() > 0) die(ob_get_clean());

	function sandbox()
	{
		global $db, $typhoon, $sessions, $users; // add any globals you want here, maybe you want a "$data" global etc. you can also change "$typhoon['data']['private']" to "$data['private']"
		require(func_get_arg(0));
	}

	//
	// Create temporary XHTML object to import <body> from layout!
	//
	$xhtml = DOMImplementation::createDocument(null, 'html', DOMImplementation::createDocumentType('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'));

	//
	// Import additional XML nodes from <head> section in page file eg. <meta> and <link> tags!
	//
	if (isset($typhoon['page']['head']))
		foreach ($typhoon['page']['head'] as $index => $node)
			if (is_object($node)) // convert any XML "nodes" (identified as objects in this array) from the page and layout XML files, to normal xhtml strings, so we just "implode" them later
				$typhoon['page']['head'][$index] = $xhtml->saveXML($xhtml->importNode($node, true));

	//
	// Get layout <body> template, store and replace <region> tags with comments -- to be processed/replaced later
	//
	$body = $xhtml->importNode($typhoon['layout']['xpath']->query('body')->item(0), true);
	$regions = $body->getElementsByTagName('region');
	while ($region = $regions->item(0))
	{
		$typhoon['regions'][0][$region->textContent] = '<!--' . $region->textContent . '-->';
		$typhoon['regions'][1][$region->textContent] = '';
		$region->parentNode->replaceChild($xhtml->createComment($region->textContent), $region);
	}

	if (ob_get_length() > 0) die(ob_get_clean());

	foreach ($typhoon['views'] as &$element)
	{
		$typhoon['data']['private'] = &$element['private'];
		sandbox($element['path'] . '/view.php');
		$typhoon['regions'][1][$element['region']] .= ob_get_contents();
		ob_clean();
	}

	ob_end_clean(); // (optional)

	header('Content-Type: text/html; charset=' . $typhoon['page']['charset']);

	echo
		'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . PHP_EOL .
		'<html xmlns="http://www.w3.org/1999/xhtml" lang="' . $typhoon['page']['lang'] . '" dir="' . $typhoon['page']['dir'] . '" xml:lang="' . $typhoon['page']['lang'] . '">' .
			'<head>' .
				'<meta http-equiv="content-type" content="text/html; charset=' . $typhoon['page']['charset'] . '" />' .
				'<title>' . htmlspecialchars($typhoon['page']['title']) . '</title>' .
				'<meta name="description" content="' . htmlspecialchars($typhoon['page']['description']) . '" />' .
				'<meta name="keywords" content="' . htmlspecialchars(implode(',', $typhoon['page']['keywords'])) . '" />' .
				'<meta name="robots" content="' . $typhoon['page']['robots'] . '" />' .
				'<meta http-equiv="content-language" content="' . $typhoon['page']['lang'] . '" />' .
				(isset($typhoon['page']['canonical']) ? '<link rel="canonical" href="' . $typhoon['page']['canonical'] . '" />' : '') .
				(isset($typhoon['page']['head']) ? implode('', $typhoon['page']['head']) : '') .
				'<style type="text/css">' .
					$typhoon['page']['style'] .
				'</style>' .
				'<script type="text/javascript">/*<![CDATA[*/' .
					$typhoon['page']['script'] .
				'/*]]>*/</script>' .
			'</head>',
			str_replace($typhoon['regions'][0], $typhoon['regions'][1], $xhtml->saveXML($body)),
		'</html>';
